﻿using System;
using System.Collections.Generic;
using System.Linq;
using ALPackage;
using JetBrains.Annotations;

namespace HYOK
{
    /// <summary>
    /// Np中的提示类型，注意实际用于比对的使用需要使用二进制处理
    /// 此处最多配置32个枚举类型！
    /// </summary>
    public enum ENoticeType
    {
        NORMAL,     //日常的基础类型提示
        ONLY_HOME,  //只在乐园处理
        SPACE, //大地图处理
        CHAPTER, //章节地图
    }

    /// <summary>
    /// 这边直接加好几种notice汇总类型静态数据
    /// 后续dealer不要每次new数组，直接来这边取值不会出错
    /// </summary>
    public static class NPNoticeType
    {
        //默认全部地方都展示
        public static ENoticeType[] g_NormalNoticeTypeArr = { ENoticeType.NORMAL, ENoticeType.ONLY_HOME, ENoticeType.SPACE, ENoticeType.CHAPTER };
        //仅在大地图或者乐园展示的
        public static ENoticeType[] g_CityMainOrSpaceTypeArr = { ENoticeType.ONLY_HOME, ENoticeType.SPACE };
        //仅在乐园展示的
        public static ENoticeType[] g_CityMainTypeArr = { ENoticeType.ONLY_HOME };
        public static ENoticeType[] g_ChapterWorldTypeArr = { ENoticeType.CHAPTER };
    }
    
    /**********************
     * UI提示信息管理对象
     **/
    public class NPUINoticeMgr
    {
        private static NPUINoticeMgr _g_instance = new NPUINoticeMgr();
        public static NPUINoticeMgr instance
        {
            get
            {
                if (null == _g_instance)
                    _g_instance = new NPUINoticeMgr();

                return _g_instance;
            }
        }

        //优先处理的队列
        [NotNull]private List<_ANPUINoticeDealer> _m_lPriorityDealerList;
        private bool _m_bIsPriorityDealing;

#if UNITY_EDITOR
        //编辑器下方便检查问题存储的当前处理对象
        private _ANPUINoticeDealer _m_dCurNoticeDealer;
#endif

        //是否允许处理提示信息，如果不允许在开启的时候会自动开始继续执行
        private bool _m_bCanDealNotice;

        //处理队列
        [NotNull]private List<_ANPUINoticeDealer> _m_lDealerList;
        //是否正在处理普通队列的节点中，注意！这个变量不包含优先级节点
        private bool _m_bIsDealing;
        //是否允许优先节点展示，这个变量只在已经展示的节点上设置。是为了避免在已经展示普通节点的情况下展示了优先节点
        private bool _m_bCanPriorityDealing;

        //处理完成之后的后续处理对象
        private Action _m_dDoneAllNoticeDelegate;

        //允许展示的提示类型二进制比对值
        private int _m_iEnableNoticeBinaryValue;
        //每个类型中允许展示的统计次数，避免多次允许一次取消就导致取消的情况
        private int[] _m_arrEnableNoticeCount;
        private int _m_iEnumCount = ALCommon.getEnumCount(typeof(ENoticeType));


        public NPUINoticeMgr()
        {
            _m_lPriorityDealerList = new List<_ANPUINoticeDealer>();
            _m_lDealerList = new List<_ANPUINoticeDealer>();
            _m_bCanDealNotice = true;

            _m_dDoneAllNoticeDelegate = null;

            _m_bIsDealing = false;
            _m_bIsPriorityDealing = false;

            _m_iEnableNoticeBinaryValue = 0;
            _m_arrEnableNoticeCount = new int[32];
        }

        #region 状态设置
        /// <summary>
        /// 设置无法进行提示处理
        /// </summary>
        public void setNoticeDisable()
        {
            _m_bCanDealNotice = false;
        }

        /// <summary>
        /// 设置无法进行提示处理
        /// </summary>
        public void setNoticeEnable()
        {
            if (_m_bCanDealNotice)
                return ;

            _m_bCanDealNotice = true;

            //此时如果不在处理中则尝试开启
            if(!_m_bIsDealing)
                _dealTryPopNoticeNextFrame();
        }

        /// <summary>
        /// 添加有效的展示类型
        /// </summary>
        /// <param name="_type"></param>
        public void addEnableNotice(ENoticeType[] _type)
        {
            if (null == _type)
                return;

            for(int i = 0; i < _type.Length; i++)
            {
                addEnableNotice(_type[i]);
            }
        }
        public void addEnableNotice(ENoticeType _type)
        {
            int idx = (int)_type;

            //左移做或处理，此时不需要判断原来是否有效，都做处理不会错
            _m_iEnableNoticeBinaryValue = _m_iEnableNoticeBinaryValue | (1 << idx);
            //增加次数
            _m_arrEnableNoticeCount[idx] = _m_arrEnableNoticeCount[idx] + 1;
        }
        public void addEnableNotice(int _noticeTypeValue)
        {
            //左移做或处理，此时不需要判断原来是否有效，都做处理不会错
            _m_iEnableNoticeBinaryValue = _m_iEnableNoticeBinaryValue | _noticeTypeValue;
            //增加次数
            for(int i = 0; i < _m_iEnumCount; i++)
            {
                if((_noticeTypeValue & (1 << i)) != 0)
                {
                    _m_arrEnableNoticeCount[i] = _m_arrEnableNoticeCount[i] + 1;
                }
            }
        }
        /// <summary>
        /// 去除对应有效的展示类型
        /// </summary>
        /// <param name="_type"></param>
        public void rmvEnableNotice(ENoticeType[] _type)
        {
            if (null == _type)
                return;

            for (int i = 0; i < _type.Length; i++)
            {
                rmvEnableNotice(_type[i]);
            }
        }
        public void rmvEnableNotice(ENoticeType _type)
        {
            int idx = (int)_type;

            //增加次数
            _m_arrEnableNoticeCount[idx] = _m_arrEnableNoticeCount[idx] - 1;

            //当数量已经到0的时候，需要从比对中去除
            if (_m_arrEnableNoticeCount[idx] == 0)
            {
                _m_iEnableNoticeBinaryValue = _m_iEnableNoticeBinaryValue & ~(1 << idx);
            }

            //判断是否低于0，是则弹错误提示
            if(_m_arrEnableNoticeCount[idx] < 0)
            {
                ALLog.Error($"notice type [{_type}] count < 0!");
                _m_arrEnableNoticeCount[idx] = 0;
            }
        }
        public void rmvEnableNotice(int _noticeTypeValue)
        {
            //增加次数
            for (int i = 0; i < _m_iEnumCount; i++)
            {
                if ((_noticeTypeValue & (1 << i)) != 0)
                {
                    _m_arrEnableNoticeCount[i] = _m_arrEnableNoticeCount[i] - 1;

                    //当数量已经到0的时候，需要从比对中去除
                    if (_m_arrEnableNoticeCount[i] == 0)
                    {
                        _m_iEnableNoticeBinaryValue = _m_iEnableNoticeBinaryValue & ~(1 << i);
                    }

                    //判断是否低于0，是则弹错误提示
                    if (_m_arrEnableNoticeCount[i] < 0)
                    {
                        ALLog.Error($"notice type [{i}] count < 0!");
                        _m_arrEnableNoticeCount[i] = 0;
                    }
                }
            }
        }

        /// <summary>
        /// 带入弹窗提示对应的二进制标识，判断是否允许弹出对应的提示
        /// </summary>
        /// <param name="_noticeValue"></param>
        public bool canShowNotice(ENoticeType _noticeType)
        {
            if ((_m_iEnableNoticeBinaryValue & (1 << (int)_noticeType)) != 0)
                return true;

            return false;
        }
        public bool canShowNotice(ENoticeType[] _noticeTypeArr)
        {
            if (null == _noticeTypeArr)
                return false;

            for(int i = 0; i < _noticeTypeArr.Length; i++)
            {
                if (canShowNotice(_noticeTypeArr[i]))
                    return true;
            }

            return false;
        }
        public bool canShowNotice(int _noticeValue)
        {
            if((_m_iEnableNoticeBinaryValue & _noticeValue) != 0)
                return true;

            return false;
        }
        #endregion

        /************
         * 尝试增加新的处理对象 
         **/
        public void addDealer(_ANPUINoticeDealer _dealer)
        {
            if(null == _dealer)
                return;

            //stage 和ui scene 都可看则处理
            if(_dealer.isPriorityDealer)
            {
                _m_lPriorityDealerList.Add(_dealer);
                //尝试弹出提示
                _dealTryPopPriorityNoticeNextFrame();
            }
            else
            {
                _m_lDealerList.Add(_dealer);
                //尝试弹出提示
                _dealTryPopNoticeNextFrame();
            }
        }

        /// <summary>
        /// 在处理过程中进行的中间处理，此时需要进行判断处理
        /// </summary>
        /// <param name="_doneDelegate"></param>
        public void dealTryPopNotice(Action _doneDelegate = null)
        {
            ////引导中不弹出，直接结束
            //if (Game.instance.isInTutorial)
            //{
            //    if (null != _doneDelegate)
            //        _doneDelegate();
            //    return;
            //}


            //判断是否在处理过程，如在则不进行处理
            if (_m_bIsDealing || _m_bIsPriorityDealing)
            {
                _addDoneDelegate(_doneDelegate);
                return;
            }

            //调用直接弹出的处理
            directDealTryPopNotice(_doneDelegate);
        }
        /// <summary>
        /// 直接弹出notice的处理，这个函数不受其他状态的控制和判断，直接进行弹出处理
        /// </summary>
        /// <param name="_doneDelegate"></param>
        public void directDealTryPopNotice(Action _doneDelegate = null)
        {
            _addDoneDelegate(_doneDelegate);

            //优先弹出优先提示，如果成功弹出优先节点则不做后续处理
            if (__popNextPriorityNotice())
                return;

            //尝试弹出非优先队列
            __popNextNotice();

            //检查是否全部完成
            _checkDone();
        }

        /// <summary>
        /// 检查是否完成所有处理，是则处理后续函数
        /// </summary>
        /// <param name="_idleAction"></param>
        public void checkDone(Action _idleAction)
        {
            if (!_m_bIsDealing && !_m_bIsPriorityDealing)
            {
                //如果无需要处理的则处理完成
                if (null != _idleAction)
                    _idleAction();
                _idleAction = null;
            }
        }

        /// <summary>
        /// 根据实例从队列中删除
        /// </summary>
        public void removeDealer(_ANPUINoticeDealer _dealer)
        {
            if(null == _dealer)
                return;
            
            for(int i = _m_lDealerList.Count - 1; i >= 0; i--)
            {
                if(_m_lDealerList[i] == _dealer)
                {
                    _m_lDealerList.RemoveAt(i);
                }
            }

            for(int i = _m_lPriorityDealerList.Count - 1; i >= 0; i--)
            {
                if(_m_lPriorityDealerList[i] == _dealer)
                {
                    _m_lPriorityDealerList.RemoveAt(i);
                }
            }
        }
        
        /// <summary>
        /// 根据类型从队列中删除
        /// </summary>
        public void removeDealerByType(Type _type)
        {
            if(null == _type)
                return;
            
            _ANPUINoticeDealer tmpDealer = null;
            for(int i = _m_lDealerList.Count - 1; i >= 0; i--)
            {
                tmpDealer = _m_lDealerList[i];
                if(null != tmpDealer && tmpDealer.GetType() == _type)
                {
                    _m_lDealerList.RemoveAt(i);
                }
            }

            for(int i = _m_lPriorityDealerList.Count - 1; i >= 0; i--)
            {
                tmpDealer = _m_lPriorityDealerList[i];
                if(null != tmpDealer && tmpDealer.GetType() == _type)
                {
                    _m_lPriorityDealerList.RemoveAt(i);
                }
            }
        }
        
        /// <summary>
        /// 根据字符串从队列中删除
        /// </summary>
        public void removeDealerByTag(string _strTag)
        {
            if(string.IsNullOrEmpty(_strTag))
                return;
            
            _ANPUINoticeDealer tmpDealer = null;
            for(int i = _m_lDealerList.Count - 1; i >= 0; i--)
            {
                tmpDealer = _m_lDealerList[i];
                if(null == tmpDealer || string.IsNullOrEmpty(tmpDealer.strTag))
                    continue;
                
                if(tmpDealer.strTag == _strTag)
                {
                    _m_lDealerList.RemoveAt(i);
                }
            }

            for(int i = _m_lPriorityDealerList.Count - 1; i >= 0; i--)
            {
                tmpDealer = _m_lPriorityDealerList[i];
                if(null == tmpDealer || string.IsNullOrEmpty(tmpDealer.strTag))
                    continue;
                
                if(tmpDealer.strTag == _strTag)
                {
                    _m_lPriorityDealerList.RemoveAt(i);
                }
            }
        }
        
        /*****************
         * 设置结束的处理
         **/
        protected void _addDoneDelegate(Action _delegate)
        {
            if (null == _delegate)
                return;

            if (null == _m_dDoneAllNoticeDelegate)
                _m_dDoneAllNoticeDelegate = _delegate;
            else
                _m_dDoneAllNoticeDelegate += _delegate;
        }

        //添加了处理对象后的尝试弹出提示处理
        protected void _dealTryPopPriorityNoticeNextFrame()
        {
            //下一帧才处理展示
            ALCommonActionMonoTask.addNextFrameTask(() => { _tryPopPriorityNotice(); }); //下一帧显示
        }
        protected void _dealTryPopNoticeNextFrame()
        {
            //下一帧才处理展示
            ALCommonActionMonoTask.addNextFrameTask(() => { _tryPopNotice(); }); //下一帧显示
        }

        /** 弹出提示信息 */
        protected void _tryPopPriorityNotice()
        {
            //if (Game.instance.isInTutorial)
            //    return;

            if (__tryDealPopPriorityNotice())
            {
                //检查是否全部完成
                _checkDone();
            }
        }
        protected void _tryPopNotice()
        {
            //if (Game.instance.isInTutorial)
            //    return;

            //优先先弹出优先提示
            if (__tryDealPopPriorityNotice())
                return;

            //判断是否在处理过程，如在则不进行处理
            if (_m_bIsDealing || _m_bIsPriorityDealing)
                return;

            //弹出提示
            __popNextNotice();

            //检查是否全部完成
            _checkDone();
        }
        /// <summary>
        /// 尝试处理弹出优先提示，返回是否有进行实际处理
        /// </summary>
        private bool __tryDealPopPriorityNotice()
        {
            //判断是否在处理过程，如在则不进行处理
            if (_m_bIsPriorityDealing)
                return false;

            //如果在执行非优先队列，且不可插入优先处理的时候不做处理
            if (_m_bIsDealing && !_m_bCanPriorityDealing)
                return false;

            //弹出提示
            return __popNextPriorityNotice();
        }

        /** 尝试弹出下一个对应提示 */

        /// <summary>
        /// 在优先节点完成的时候调用的展示下一个节点的处理
        /// </summary>
        protected void _onPriorityNoticeDone()
        {
            //优先弹出优先提示
            if (__popNextPriorityNotice())
                return;

            //在非优先并没有处理的时候，尝试弹出非优先队列
            if (!_m_bIsDealing)
                __popNextNotice();

            //检查是否全部完成
            _checkDone();
        }
        /// <summary>
        /// 在非优先节点结束的时候调用的处理下一个函数
        /// 会重置在非优先级节点情况下的优先级节点是否有效的状态
        /// 同时根据是否在处理优先级节点判断是否需要展示优先级节点
        /// </summary>
        protected void _onNoPriorityNoticeDone()
        {
            //允许优先插入
            _m_bCanPriorityDealing = true;
            //如果此时在进行优先节点的处理，则重置状态
            if (_m_bIsPriorityDealing)
            {
                //如果当前有优先节点在处理，需要重置不同节点处理状态
                _m_bIsDealing = false;
                return;
            }

            //先优先弹出优先提示
            if (__popNextPriorityNotice())
            {
                //如果当前优先节点处理成功，需要重置不同节点处理状态
                _m_bIsDealing = false;
                return;
            }

            //尝试弹出非优先队列
            __popNextNotice();

            //检查是否全部完成
            _checkDone();
        }

        /** 弹出提示信息 */
        private bool __popNextPriorityNotice()
        {
            //如果不允许触发notice则直接返回
            if (!_m_bCanDealNotice)
            {
                //直接当做没有提示返回
                //设置不在处理中
                _m_bIsPriorityDealing = false;

                return false;
            }

            _ANPUINoticeDealer dealer = null;

            for(int i = 0; i < _m_lPriorityDealerList.Count; )
            {
                dealer = _m_lPriorityDealerList[i];

                //如果数据无效则删除
                if (null == dealer || dealer.isDone)
                {
                    ALLog.Error($"Notice: {dealer} in list is done!");
                    _m_lPriorityDealerList.RemoveAt(i);

                    continue;
                }

                //找到不为空，且真正被允许在此时弹出的窗口才跳出
                if (dealer != null && dealer.canCurShow
                    && (canShowNotice(dealer.noticeType)))
                {
                    _m_lPriorityDealerList.RemoveAt(i);
                    break;
                }

                //全部轮完都没有选到，dealer置空，否则会选最后一个
                dealer = null;

                //累加下标
                i++;
            }

            //进行处理
            if(null != dealer)
            {
                //设置在处理中
                _m_bIsPriorityDealing = true;
#if UNITY_EDITOR
                //编辑器下方便检查问题存储的当前处理对象
                _m_dCurNoticeDealer = dealer;
#endif
                dealer.showNotice();

                return true;
            }
            else
            {
                //设置不在处理中
                _m_bIsPriorityDealing = false;

                return false;
            }
        }
        /** 弹出提示信息 */
        private bool __popNextNotice()
        {
            //如果不允许触发notice则直接返回
            if(!_m_bCanDealNotice)
            {
                //直接当做没有提示返回
                //设置不在处理中
                _m_bIsDealing = false;
                _m_bCanPriorityDealing = true;

                return false;
            }

            _ANPUINoticeDealer dealer = null;

            for(int i = 0; i < _m_lDealerList.Count; )
            {
                dealer = _m_lDealerList[i];

                //如果数据无效则删除
                if (null == dealer || dealer.isDone)
                {
                    ALLog.Error($"Notice: {dealer} in list is done!");
                    _m_lDealerList.RemoveAt(i);

                    continue;
                }

                //找到不为空，且真正被允许在此时弹出的窗口才跳出
                if (dealer != null && dealer.canCurShow
                    && (canShowNotice(dealer.noticeType)))
                {
                    _m_lDealerList.RemoveAt(i);
                    break;
                }

                //全部轮完都没有选到，dealer置空，否则会选最后一个
                dealer = null;

                //累加下标
                i++;
            }

            //进行处理
            if(null != dealer)
            {
                //设置在处理中
                _m_bIsDealing = true;
                _m_bCanPriorityDealing = dealer.canPlayPriority;
#if UNITY_EDITOR
                //编辑器下方便检查问题存储的当前处理对象
                _m_dCurNoticeDealer = dealer;
#endif

                dealer.showNotice();

                return true;
            }
            else
            {
                //设置不在处理中
                _m_bIsDealing = false;
                _m_bCanPriorityDealing = true;

                return false;
            }
        }

        /** 检查是否完成所有处理 */
        private void _checkDone()
        {
            if(!_m_bIsDealing && !_m_bIsPriorityDealing)
            {
                //如果无需要处理的则处理完成
                Action doneDelegate = _m_dDoneAllNoticeDelegate;
                _m_dDoneAllNoticeDelegate = null;
                if(null != doneDelegate)
                    doneDelegate();
            }
        }
        
        /// <summary>
        /// 游戏中UI提示信息处理对象
        /// </summary>
        public abstract class _ANPUINoticeDealer
        {
#if UNITY_EDITOR
            //显示序列号，用于判断Editor情况下是否超过10秒未关闭，如果未关闭则提示
            //方便editor情况下发现问题
            private long _m_lNoticeDealSerialize;
#endif

            //实际的处理Node
            private NPGAddQueueNoticeDealerNode _m_nDealNode;

            //是否已经完结
            private bool _m_bIsDone = false;

            //返回是否已完成
            public bool isDone { get { return _m_bIsDone; } }

            //是否在这个notice中可以回退
            public virtual bool noticeCanDoESC { get { return true; } }
            
            // 这个 node 是否是全屏
            public virtual bool isNoticeFullScreen { get { return false; } } 

            // 这个 node 是否是是纯ui的
            public virtual bool isOnlyUINode { get { return false; } } 
            
            /** 设置本处理过程完结 */
            public void setDealerDone()
            {
                if (_m_bIsDone)
                    return;

                //设置已完成
                _m_bIsDone = true;
#if UNITY_EDITOR
                //刷新显示序列号，表示问题处理完毕
                _m_lNoticeDealSerialize = ALSerializeOpMgr.next();
#endif
                //触发函数
                _onDealerDone();

                //关闭节点
                if (null != _m_nDealNode)
                {
                    QueueMgr.instance.forceCloseNode(_m_nDealNode);
                    _m_nDealNode = null;
                }

                //处理下一个
                if (isPriorityDealer)
                    NPUINoticeMgr.instance._onPriorityNoticeDone();
                else
                    NPUINoticeMgr.instance._onNoPriorityNoticeDone();
            }

            //展示本节点的提示信息
            public virtual void showNotice()
            {
#if UNITY_EDITOR
                //显示序列号，用于判断Editor情况下是否超过10秒未关闭，如果未关闭则提示
                //方便editor情况下发现问题
                _m_lNoticeDealSerialize = ALSerializeOpMgr.next();
                //long tmpSerialize = _m_lNoticeDealSerialize;
                //_ANPUINoticeDealer tmpDealer = this;
                //ALCommonTaskController.CommonActionAddMonoTask(
                //    () =>
                //    {
                //        if (tmpSerialize == _m_lNoticeDealSerialize)
                //        {
                //            //弹出提示
                //            NPMesMgr.instance.showOneBtnMes($"{tmpDealer} 30秒后未结束，注意是否存在问题！", "ok", null);
                //        }
                //    }, 30f);
#endif

                //关闭原先的node
                if(null != _m_nDealNode)
                {
                    QueueMgr.instance.forceCloseNode(_m_nDealNode);
                    _m_nDealNode = null;
                }
                //开启一个新node
                _m_nDealNode = new NPGAddQueueNoticeDealerNode(this);

                //进入处理Node
                QueueMgr.instance.AddNode(_m_nDealNode);
            }

            /// <summary>
            /// 本提示对应的提示类型
            /// 默认所有界面都允许弹出
            /// 提示类型允许存在多个提示类型的特性，以更好的适应在不同位置弹出的需求
            /// </summary>
            public virtual ENoticeType[] noticeType { get { return NPNoticeType.g_NormalNoticeTypeArr; } }

            /// <summary>
            /// 是否需要蒙版遮罩
            /// </summary>
            public virtual bool needTransBk { get { return false; } }

            /// <summary>
            /// 字符串标记，可以用来根据tag删除notice
            /// </summary>
            public virtual string strTag { get { return string.Empty; } }

            //是否优先处理的对象
            public abstract bool isPriorityDealer { get; }
            public abstract bool canPlayPriority { get; }
            //是否现在可以从队列中提出来展示
            public abstract bool canCurShow { get; }

            //完结的触发函数
            protected abstract void _onDealerDone();
            //展示本节点的提示信息
            public abstract void dealShowNotice();
            //隐藏提示
            public abstract void dealHideNotice();
        }
    }
}
